//*************************************************************************************************
// TFSServer.cs
//*************************************************************************************************
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.VersionControl.Common;

namespace TFSComApi {

//*************************************************************************************************
/// <summary>
/// The main interface to a TFSServer.  It enables connecting and manipulating the contents of the
/// server.
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
[GuidAttribute("AE432687-84FD-48ef-B6E8-72DD0EE31F7A")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITFSServer
{
    void Connect(String url, String username, String password);
    String AuthenticatedUser
    {
        get;
    }
    TFSItem GetItem(String itemPath);
    TFSItem GetParentItem(String itemPath);
    ITFSWorkspace GetWorkspace(String name, String owner);
    ITFSWorkspace CreateWorkspace(String name, String owner, String comment);
    ITFSPendingChanges GetPendingChanges(String itemPath);
}

//*************************************************************************************************
//*************************************************************************************************
[ComVisible(true)]
[GuidAttribute("5676CC3A-372F-4fc3-84B3-D9EA87C11A04")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITFSWorkspace
{
    TFSWorkingFolder GetWorkingFolderForServerItem(String itemPath);
    void SetWorkingFolder(String itemPath, String localPath);
}

//*************************************************************************************************
/// <summary>
/// Used to identify the type of TFS version controled items.
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
public enum TFSItemType
{
    Any = ItemType.Any,
    File = ItemType.File,
    Folder = ItemType.Folder
}

//*************************************************************************************************
/// <summary>
/// The properties of a TFS version controlled item (file or folder).
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential)]
public struct TFSItem
{
    //*********************************************************************************************
    //*********************************************************************************************
    internal TFSItem(String itemPath, ItemType itemType, int itemId, int changesetId, int deletionId,
                    byte[] hashValue, long contentLength, int encoding)
    {
        ItemPath = itemPath;
        ItemType = (TFSItemType)itemType;
        ItemId = itemId;
        ChangesetId = changesetId;
        DeletionId = deletionId;
//TODO:        HashValue = hashValue;
        ContentLength = contentLength;
        Encoding = encoding;
    }

    [MarshalAs(UnmanagedType.BStr)]
    public String ItemPath;
    public TFSItemType ItemType;
    public int ItemId;
    public int ChangesetId;
    public int DeletionId;
//TODO:    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_I1)]
//    public byte[] HashValue;
    public long ContentLength;
    public int Encoding;
}

//*************************************************************************************************
/// <summary>
/// The interface for a collection of pending changes on TFS version controlled items.
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
[GuidAttribute("B8364ECA-D72A-4aa4-8838-9A3E37675B06")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITFSPendingChanges
{
    int Count
    {
        get;
    }
    TFSPendingChange GetChange(int i);
}

//*************************************************************************************************
/// <summary>
/// An enumeration that identifies what kind a pending set (a group of pending changes) is.  It can
/// either be from an active workspace or it can be from a shelveset.
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
public enum TFSPendingSetType
{
    Workspace = PendingSetType.Workspace,
    Shelveset = PendingSetType.Shelveset
}

//*************************************************************************************************
/// <summary>
/// An enumeration that identifies the various types of pending changes that TFS supports.  These
/// can be or'd together for compoud change types (like Edit | Rename | Lock).
/// </summary>
//*************************************************************************************************
[Flags]
[ComVisible(true)]
public enum TFSChangeType
{
    None = ChangeType.None,
    Add = ChangeType.Add,
    Edit = ChangeType.Edit,
    Encoding = ChangeType.Encoding,
    Rename = ChangeType.Rename,
    Delete = ChangeType.Delete,
    Undelete = ChangeType.Undelete,
    Branch = ChangeType.Branch,
    Merge = ChangeType.Merge,
    Lock = ChangeType.Lock
}

//*************************************************************************************************
/// <summary>
/// An enumeration that lists the kinds of locks TFS supports on version controlled files.
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
public enum TFSLockLevel
{
    None = LockLevel.None,
    Checkin = LockLevel.Checkin,
    CheckOut = LockLevel.CheckOut,
    Unchanged = LockLevel.Unchanged
}
 
//*************************************************************************************************
/// <summary>
/// Information about a pending change in TFS.
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential)]
public struct TFSPendingChange
{
    //*********************************************************************************************
    //*********************************************************************************************
    internal TFSPendingChange(int changeId, ChangeType changeType, String serverItemPath,
                    ItemType itemType, int itemId, int changesetId, int deletionId,
                    LockLevel lockLevel, DateTime creationDate, byte[] hashValue,
                    int encoding, String localItemPath, String sourceServerItemPath,
                    String sourceLocalItemPath, String workspaceName, String workspaceOwnerName,
                    String workspaceComputer, PendingSetType workspaceType)
    {
        ChangeId = changeId;
        ChangeType = (TFSChangeType)changeType;
        ServerItemPath = serverItemPath;
        ItemType = (TFSItemType)itemType;
        ItemId = itemId;
        ChangesetId = changesetId;
        DeletionId = deletionId;
        LockLevel = (TFSLockLevel)lockLevel;
        CreationDate = creationDate;
//TODO:        HashValue = hashValue;
        Encoding = encoding;
        LocalItemPath = localItemPath;
        SourceServerItemPath = sourceServerItemPath;
        SourceLocalItemPath = sourceLocalItemPath;
        WorkspaceName = workspaceName;
        WorkspaceOwnerName = workspaceOwnerName;
        WorkspaceComputer = workspaceComputer;
        WorkspaceType = (TFSPendingSetType)workspaceType;
    }

    public int ChangeId;
    public TFSChangeType ChangeType;
    [MarshalAs(UnmanagedType.BStr)]
    public String ServerItemPath;
    public TFSItemType ItemType;
    public int ItemId;
    public int ChangesetId;
    public int DeletionId;
    public TFSLockLevel LockLevel;
    public DateTime CreationDate;
//TODO:    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_I1)]
//    public byte[] HashValue;
    public int Encoding;
    [MarshalAs(UnmanagedType.BStr)]
    public String LocalItemPath;
    [MarshalAs(UnmanagedType.BStr)]
    public String SourceServerItemPath;
    [MarshalAs(UnmanagedType.BStr)]
    public String SourceLocalItemPath;
    [MarshalAs(UnmanagedType.BStr)]
    public String WorkspaceName;
    [MarshalAs(UnmanagedType.BStr)]
    public String WorkspaceOwnerName;
    [MarshalAs(UnmanagedType.BStr)]
    public String WorkspaceComputer;
    public TFSPendingSetType WorkspaceType;
}

//*************************************************************************************************
/// <summary>
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
public enum TFSWorkingFolderType
{
    Map = WorkingFolderType.Map,
    Cloak = WorkingFolderType.Cloak
}

//*************************************************************************************************
//*************************************************************************************************
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential)]
public struct TFSWorkingFolder
{
    //*********************************************************************************************
    //*********************************************************************************************
    internal TFSWorkingFolder(WorkingFolderType type, String localItem, String serverItem)
    {
        Type = (TFSWorkingFolderType)type;
        LocalPath = localItem;
        ServerItemPath = serverItem;
    }
    public TFSWorkingFolderType Type;
    [MarshalAs(UnmanagedType.BStr)]
    public String LocalPath;
    [MarshalAs(UnmanagedType.BStr)]
    public String ServerItemPath;
}

//*************************************************************************************************
/// <summary>
/// The main server object in this COM OM for TFS.  This allows you to connect to TFS and to
/// manipulate the data it contains.
/// </summary>
//*************************************************************************************************
[ComVisible(true)]
[GuidAttribute("02F494FE-6ED4-4787-8D27-46EAFDC7C217")]
public class TFSServer : ITFSServer
{
    //*********************************************************************************************
    /// <summary>
    /// Connect to the TFS server and authenticate the user.
    /// </summary>
    /// <param name="url">The TFS server connection string.</param>
    /// <param name="username">The user name to connect as (null means use current credentials.</param>
    /// <param name="password">The password to use if the username is not null.</param>
    //*********************************************************************************************
    public void Connect(String url, String username, String password)
    {
        if (username == null)
        {
            m_tfs = new TeamFoundationServer(url);
        }
        else
        {
            m_tfs = new TeamFoundationServer(url, new System.Net.NetworkCredential(username, password));
        }
        m_tfs.Authenticate();
        m_vcs = (VersionControlServer)m_tfs.GetService(typeof(VersionControlServer));
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public String AuthenticatedUser
    {
        get { return m_tfs.AuthenticatedUserName; }
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public TFSItem GetItem(String itemPath)
    {
        Item item = m_vcs.GetItem(itemPath);
        return new TFSItem(item.ServerItem, item.ItemType, item.ItemId, item.ChangesetId,
                            item.DeletionId, item.HashValue, item.ContentLength, item.Encoding);
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public TFSItem GetParentItem(String itemPath)
    {
        return GetItem(VersionControlPath.GetFolderName(itemPath));
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public ITFSWorkspace GetWorkspace(String name, String owner)
    {
        Workspace ws = m_vcs.GetWorkspace(name, owner);
        return new TFSWorkspace(this, ws);
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public ITFSWorkspace CreateWorkspace(String name, String owner, String comment)
    {
        Workspace workspace = m_vcs.CreateWorkspace(name, owner, comment);
        return new TFSWorkspace(this, workspace);
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public ITFSPendingChanges GetPendingChanges(String itemPath)
    {
        PendingSet[] sets = m_vcs.GetPendingSets(new String[] { itemPath }, RecursionType.None);

        List<TFSPendingChange> pendingChanges = new List<TFSPendingChange>();
        foreach (PendingSet set in sets)
        {
            foreach (PendingChange pc in set.PendingChanges)
            {
                TFSPendingChange tpc = new TFSPendingChange(pc.PendingChangeId, pc.ChangeType,
                                                        pc.ServerItem, pc.ItemType, pc.ItemId,
                                                        pc.Version, pc.DeletionId, pc.LockLevel,
                                                        pc.CreationDate, pc.HashValue, pc.Encoding,
                                                        pc.LocalItem, pc.SourceServerItem,
                                                        pc.SourceLocalItem, set.Name, set.OwnerName,
                                                        set.Computer, set.Type);
                pendingChanges.Add(tpc);
            }
        }
        return new TFSPendingChanges(pendingChanges);
    }

    private TeamFoundationServer m_tfs;
    private VersionControlServer m_vcs;
}

//*************************************************************************************************
//*************************************************************************************************
public class TFSWorkspace : ITFSWorkspace
{
    //*********************************************************************************************
    //*********************************************************************************************
    public TFSWorkspace(TFSServer server, Workspace workspace)
    {
        m_tfs = server;
        m_workspace = workspace;
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public TFSWorkingFolder GetWorkingFolderForServerItem(String itemPath)
    {
        WorkingFolder wf = m_workspace.GetWorkingFolderForServerItem(itemPath);
        return new TFSWorkingFolder(wf.Type, wf.LocalItem, wf.ServerItem);
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public void SetWorkingFolder(String itemPath, String localPath)
    {
        m_workspace.Map(itemPath, localPath);
    }

    TFSServer m_tfs;
    Workspace m_workspace;
}

    
//*************************************************************************************************
/// <summary>
/// A class that contains a list of pending changes.  It is used to enumerate a group of pending
/// changes returned from the server.
/// </summary>
//*************************************************************************************************
public class TFSPendingChanges : ITFSPendingChanges
{
    //*********************************************************************************************
    //*********************************************************************************************
    public TFSPendingChanges(List<TFSPendingChange> pcs)
    {
        m_pendingChanges = pcs;
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public int Count
    {
        get { return m_pendingChanges.Count; }
    }

    //*********************************************************************************************
    //*********************************************************************************************
    public TFSPendingChange GetChange(int i)
    {
        return m_pendingChanges[i];
    }

    private List<TFSPendingChange> m_pendingChanges;
}

} // namespace TFSComApi
